package com.todo.event.activity.application.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.api.client.util.Lists;
import com.todo.common.common.entity.response.page.PageResult;
import com.todo.common.common.enums.BizErrorCodeEnum;
import com.todo.common.common.exception.BizException;
import com.todo.common.utils.DateUtils;
import com.todo.common.utils.StringUtils;
import com.todo.component.redis.RedisService;
import com.todo.event.activity.application.convert.EventActivityConvert;
import com.todo.event.activity.application.entity.bo.DecisionEventRuleBO;
import com.todo.event.activity.application.entity.bo.EventDecisionBO;
import com.todo.event.activity.application.entity.condition.EventDecisionConditionBO;
import com.todo.event.activity.application.service.EventDecisionService;
import com.todo.event.activity.common.base.EventKeyBuild;
import com.todo.event.activity.common.enums.DecisionTypeEnum;
import com.todo.event.activity.common.enums.EventDecisionStatusEnum;
import com.todo.event.activity.common.enums.EventFieldTypeEnum;
import com.todo.event.activity.common.enums.SelectOperateEnum;
import com.todo.common.utils.TodoCommonUtils;
import com.todo.event.activity.infrastructure.entity.condition.EventDecisionCondition;
import com.todo.event.activity.infrastructure.entity.po.EventDecisionPO;
import com.todo.event.activity.infrastructure.repository.EventDecisionRepository;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author azhebuxing
 * @date 2025/1/7 13:14
 * @description
 */
@Slf4j
@Service
public class EventDecisionServiceImpl implements EventDecisionService {

    @Resource
    private EventDecisionRepository eventDecisionRepository;
    @Resource
    private RedisService redisService;


    @Override
    public Boolean insertEventDecision(EventDecisionBO eventDecisionBO) {
        if (eventDecisionBO == null) {
            return false;
        }
        if (eventDecisionBO.getDecisionType().equals(DecisionTypeEnum.MULTIPLE)) {
            if (eventDecisionBO.getSecondEvent() == null || eventDecisionBO.getIntervalTime() == null) {
                log.error("ContentInfoService#insertContentInfo 多选项决策类型必须同时设置第二事件和间隔时间 eventDecisionBO:【{}】", JSONObject.toJSONString(eventDecisionBO));
                throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "多选项决策类型必须同时设置第二事件和间隔时间");
            }
        }
        //决策规则基础验证
        boolean ruleCheck = decisionRuleCheck(eventDecisionBO);
        if (!ruleCheck) {
            log.error("ContentInfoService#insertEventDecision 决策规则验证失败 eventDecisionBO:【{}】", JSONObject.toJSONString(eventDecisionBO));
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "决策规则验证失败");
        }

        EventDecisionPO alreadyEventDecisionPO = eventDecisionRepository.queryEventDecisionByName(eventDecisionBO.getName());
        if (alreadyEventDecisionPO != null) {
            log.error("ContentInfoService#insertEventDecision 决策名称已存在 eventDecisionBO:【{}】", JSONObject.toJSONString(eventDecisionBO));
            throw new BizException(BizErrorCodeEnum.OPERATION_FAILED, "决策名称已存在");
        }
        eventDecisionBO.setStatus(EventDecisionStatusEnum.DRAFT);
        EventDecisionPO eventDecisionPO = EventActivityConvert.converttoEventDecisionPO(eventDecisionBO);
        Boolean inserted = eventDecisionRepository.insertEventDecisionPO(eventDecisionPO);
        if (inserted) {
            String decisionKey = EventKeyBuild.buildEventDecisionKey(eventDecisionBO.getId());
            redisService.setOneDay(decisionKey, JSONObject.toJSONString(eventDecisionBO));
        }
        return inserted;
    }


    @Override
    public Boolean updateEventDecision(EventDecisionBO eventDecisionBO) {
        if (eventDecisionBO == null || eventDecisionBO.getId() == null) {
            return false;
        }
        if (eventDecisionBO.getDecisionType().equals(DecisionTypeEnum.MULTIPLE)) {
            if (eventDecisionBO.getSecondEvent() == null || eventDecisionBO.getIntervalTime() == null) {
                log.error("ContentInfoService#updateContentInfo 多选项决策类型必须同时设置第二事件和间隔时间 eventDecisionBO:【{}】", JSONObject.toJSONString(eventDecisionBO));
                throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "多选项决策类型必须同时设置第二事件和间隔时间");
            }
        }
        //决策规则基础验证
        boolean ruleCheck = decisionRuleCheck(eventDecisionBO);
        if (!ruleCheck) {
            log.error("ContentInfoService#insertContentInfo 决策规则验证失败 eventDecisionBO:【{}】", JSONObject.toJSONString(eventDecisionBO));
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "决策规则验证失败");
        }
        EventDecisionBO alreadyEventDecisionBO = queryEventDecisionById(eventDecisionBO.getId());
        if (alreadyEventDecisionBO == null) {
            log.error("ContentInfoService#updateContentInfo 决策不存在 eventDecisionBO:【{}】", JSONObject.toJSONString(eventDecisionBO));
            throw new BizException(BizErrorCodeEnum.OPERATION_FAILED, "决策不存在");
        }
        if (!alreadyEventDecisionBO.getStatus().equals(EventDecisionStatusEnum.SHUTDOWN)) {
            log.error("ContentInfoService#updateContentInfo 决策状态不允许修改 eventDecisionBO:【{}】", JSONObject.toJSONString(eventDecisionBO));
            throw new BizException(BizErrorCodeEnum.NOT_SUPPORT_OPERATE_STATUS, "决策状态不允许修改");
        }
        if (!alreadyEventDecisionBO.getName().equals(eventDecisionBO.getName())) {
            EventDecisionPO alreadyName = eventDecisionRepository.queryEventDecisionByName(eventDecisionBO.getName());
            if (alreadyName != null) {
                log.error("ContentInfoService#updateContentInfo 决策名称已存在 eventDecisionBO:【{}】", JSONObject.toJSONString(eventDecisionBO));
                throw new BizException(BizErrorCodeEnum.OPERATION_FAILED, "决策名称已存在");
            }
        }
        eventDecisionBO.setStatus(EventDecisionStatusEnum.DRAFT);
        EventDecisionPO eventDecisionPO = EventActivityConvert.converttoEventDecisionPO(eventDecisionBO);
        Boolean updated = eventDecisionRepository.updateEventDecisionPOById(eventDecisionPO);
        if (updated) {
            String decisionKey = EventKeyBuild.buildEventDecisionKey(eventDecisionBO.getId());
            redisService.del(decisionKey);
        }
        return updated;
    }

    @Override
    public Boolean removeEventDecision(Long id) {
        if (id == null || id <= 0) {
            return false;
        }
        EventDecisionBO eventDecisionBO = queryEventDecisionById(id);
        if (eventDecisionBO == null) {
            return true;
        }
        Boolean removed = eventDecisionRepository.removeEventDecisionPOById(id);
        if (removed) {
            String decisionKey = EventKeyBuild.buildEventDecisionKey(id);
            redisService.del(decisionKey);
            removeEventCodeRelation(eventDecisionBO);
        }
        return removed;
    }

    private void removeEventCodeRelation(EventDecisionBO eventDecisionBO) {
        String firstEvent = eventDecisionBO.getFirstEvent();
        String firstMapKey = EventKeyBuild.buildEventDecisionCodeMapKey(firstEvent);
        redisService.sRemove(firstMapKey, String.valueOf(eventDecisionBO.getId()));
        if (eventDecisionBO.getDecisionType().equals(DecisionTypeEnum.MULTIPLE)) {
            String secondEvent = eventDecisionBO.getSecondEvent();
            String secondMapKey = EventKeyBuild.buildEventDecisionCodeMapKey(secondEvent);
            redisService.sRemove(secondMapKey, String.valueOf(eventDecisionBO.getId()));
        }
    }

    @Override
    public EventDecisionBO queryEventDecisionById(Long id) {
        if (id == null || id <= 0) {
            return null;
        }
        String decisionKey = EventKeyBuild.buildEventDecisionKey(id);
        if (redisService.hasKey(decisionKey)) {
            String decisionJson = redisService.get(decisionKey);
            return JSONObject.parseObject(decisionJson, EventDecisionBO.class);
        }
        EventDecisionPO eventDecisionPO = eventDecisionRepository.queryEventDecisionPOById(id);
        if (eventDecisionPO != null) {
            EventDecisionBO eventDecisionBO = EventActivityConvert.convertToEventDecisionBO(eventDecisionPO);
            redisService.setOneDay(decisionKey, JSONObject.toJSONString(eventDecisionBO));
            return eventDecisionBO;
        }
        log.warn("EventDecisionService#queryEventDecisionById 查询决策失败，决策id不存在 id：【{}】", id);
        return null;
    }

    @Override
    public List<EventDecisionBO> querySingleEventListByCode(String eventCode) {
        String MapKey = EventKeyBuild.buildEventDecisionCodeMapKey(eventCode);
        Set<String> cachedValue = redisService.sMembers(MapKey);
        if (CollectionUtils.isNotEmpty(cachedValue)) {
            List<EventDecisionBO> result = new ArrayList<>();
            cachedValue.forEach(item -> {
                EventDecisionBO eventDecisionBO = queryEventDecisionById(Long.valueOf(item));
                if (eventDecisionBO != null && eventDecisionBO.getStatus().equals(EventDecisionStatusEnum.RUNNING) && eventDecisionBO.getDecisionType().equals(DecisionTypeEnum.SINGLE)) {
                    result.add(eventDecisionBO);
                }
            });
            return result;
        }
        List<EventDecisionPO> eventDecisionPOList = eventDecisionRepository.queryEventDecisionByEventCode(eventCode);
        if (CollectionUtils.isEmpty(eventDecisionPOList)) {
            log.warn("EventDecisionService#queryFirstEventListByCode 查询首决策失败，决策不存在 eventCode：【{}】", eventCode);
            return Collections.emptyList();
        }
        eventDecisionPOList.forEach(item -> {
            if (item != null) {
                redisService.sAdd(MapKey, String.valueOf(item.getId()));
            }
        });
        return eventDecisionPOList.stream()
                .filter(item -> item.getDecisionType().equals(DecisionTypeEnum.SINGLE.getType()))
                .map(EventActivityConvert::convertToEventDecisionBO)
                .collect(Collectors.toList());
    }

    @Override
    public List<EventDecisionBO> querySingleEventListByCodeList(List<String> eventCodeList) {
        if (CollectionUtils.isEmpty(eventCodeList)) {
            return Collections.emptyList();
        }
        List<EventDecisionBO> result = new ArrayList<>();
        eventCodeList.forEach(item -> {
            result.addAll(querySingleEventListByCode(item));
        });
        return result;
    }

    @Override
    public List<EventDecisionBO> queryMultipleEventListByCode(String eventCode) {
        String MapKey = EventKeyBuild.buildEventDecisionCodeMapKey(eventCode);
        Set<String> cachedValue = redisService.sMembers(MapKey);
        if (CollectionUtils.isNotEmpty(cachedValue)) {
            List<EventDecisionBO> result = new ArrayList<>();
            cachedValue.forEach(item -> {
                EventDecisionBO eventDecisionBO = queryEventDecisionById(Long.valueOf(item));
                if (eventDecisionBO != null && eventDecisionBO.getDecisionType().equals(DecisionTypeEnum.MULTIPLE)) {
                    result.add(eventDecisionBO);
                }
            });
            return result;
        }
        List<EventDecisionPO> eventDecisionPOList = eventDecisionRepository.queryEventDecisionByEventCode(eventCode);
        if (CollectionUtils.isEmpty(eventDecisionPOList)) {
            log.warn("EventDecisionService#querySecondEventListByCode 查询第二决策失败，决策不存在 eventCode：【{}】", eventCode);
            return Collections.emptyList();
        }
        eventDecisionPOList.forEach(item -> {
            if (item != null) {
                redisService.sAdd(MapKey, String.valueOf(item.getId()));
            }
        });
        return eventDecisionPOList.stream()
                .filter(item -> item.getDecisionType().equals(DecisionTypeEnum.MULTIPLE.getType()))
                .map(EventActivityConvert::convertToEventDecisionBO)
                .collect(Collectors.toList());
    }

    @Override
    public List<EventDecisionBO> queryMultipleEventListByCodeList(List<String> eventCodeList) {
        if (CollectionUtils.isEmpty(eventCodeList)) {
            return Collections.emptyList();
        }
        List<EventDecisionBO> result = new ArrayList<>();
        eventCodeList.forEach(item -> {
            result.addAll(queryMultipleEventListByCode(item));
        });
        return result;
    }

    @Override
    public Boolean updateEventDecisionStatus(Long id, EventDecisionStatusEnum oldStatus, EventDecisionStatusEnum newStatus) {
        if (id == null || oldStatus == null || newStatus == null) {
            return false;
        }
        EventDecisionBO alreadyEventDecisionBO = queryEventDecisionById(id);
        if (alreadyEventDecisionBO == null) {
            log.error("EventDecisionService#updateEventDecisionStatus 决策不存在 id:【{}】", id);
            throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST, "决策不存在");
        }
        if (!alreadyEventDecisionBO.getStatus().equals(oldStatus)) {
            log.error("EventDecisionService#updateEventDecisionStatus 决策状态异常，当前状态不为操作状态，更新失败 alreadyEventDecisionBO:【{}】", JSONObject.toJSONString(alreadyEventDecisionBO));
            return false;
        }
        if (alreadyEventDecisionBO.getStatus().equals(newStatus)) {
            return true;
        }
        Boolean updated = eventDecisionRepository.updateEventDecisionStatus(id, oldStatus, newStatus);
        if (updated) {
            if (newStatus.equals(EventDecisionStatusEnum.RUNNING)) {
                DoHandleDecisionRunningStatus(alreadyEventDecisionBO);
            }
            String decisionKey = EventKeyBuild.buildEventDecisionKey(id);
            redisService.del(decisionKey);
        }
        return updated;
    }

    private void DoHandleDecisionRunningStatus(EventDecisionBO eventDecisionBO) {
        if (eventDecisionBO.getDecisionType().equals(DecisionTypeEnum.SINGLE)) {
            String firstEvent = eventDecisionBO.getFirstEvent();
            String firstMapKey = EventKeyBuild.buildEventDecisionCodeMapKey(firstEvent);
            redisService.sAdd(firstMapKey, String.valueOf(eventDecisionBO.getId()));
        }
        if (eventDecisionBO.getDecisionType().equals(DecisionTypeEnum.MULTIPLE)) {
            String firstEvent = eventDecisionBO.getFirstEvent();
            String firstMapKey = EventKeyBuild.buildEventDecisionCodeMapKey(firstEvent);
            redisService.sAdd(firstMapKey, String.valueOf(eventDecisionBO.getId()));

            String secondEvent = eventDecisionBO.getSecondEvent();
            String secondMapKey = EventKeyBuild.buildEventDecisionCodeMapKey(secondEvent);
            redisService.sAdd(secondMapKey, String.valueOf(eventDecisionBO.getId()));
        }
    }

    @Override
    public PageResult<EventDecisionBO> queryEventDecisionPageByCondition(EventDecisionConditionBO eventDecisionConditionBO) {
        if (eventDecisionConditionBO == null) {
            return new PageResult<>();
        }
        EventDecisionCondition eventDecisionCondition = EventActivityConvert.convertToEventDecisionCondition(eventDecisionConditionBO);
        PageResult<EventDecisionPO> pageResult = eventDecisionRepository.queryEventDecisionByCondition(eventDecisionCondition);
        if (pageResult == null) {
            return PageResult.of(Lists.newArrayList(), eventDecisionConditionBO.getPageIndex(), eventDecisionConditionBO.getPageSize(), 0L);
        }
        PageResult<EventDecisionBO> result = new PageResult<>();
        result.setResults(pageResult.getResults() == null ? null : pageResult.getResults().stream().map(EventActivityConvert::convertToEventDecisionBO).collect(Collectors.toList()));
        result.setHasNext(pageResult.isHasNext());
        result.setTotal(pageResult.getTotal());
        result.setPageCount(pageResult.getPageCount());
        result.setPageNum(pageResult.getPageNum());
        result.setCurrentIndex(pageResult.getCurrentIndex());
        result.setPageSize(pageResult.getPageSize());
        return result;
    }


    /**
     * 决策规则基础验证
     *
     * @param eventDecisionBO
     * @return
     */
    private boolean decisionRuleCheck(EventDecisionBO eventDecisionBO) {
        if (eventDecisionBO.getFirstEventRule() == null) {
            return false;
        }
        DecisionEventRuleBO firstEventRule = eventDecisionBO.getFirstEventRule();
        Boolean firstChecked = propertyRuleCheck(firstEventRule);
        if (!firstChecked) {
            return false;
        }
        if (eventDecisionBO.getDecisionType().equals(DecisionTypeEnum.MULTIPLE)) {
            DecisionEventRuleBO secondEventRule = eventDecisionBO.getSecondEventRule();
            Boolean secondChecked = propertyRuleCheck(secondEventRule);
            if (!secondChecked) {
                return false;
            }
        }
        return true;
    }

    /**
     * 决策规则基础验证
     *
     * @param eventRuleBO
     * @return
     */
    private Boolean propertyRuleCheck(DecisionEventRuleBO eventRuleBO) {
        if (eventRuleBO == null) {
            return true;
        }
        if (StringUtils.isNotEmpty(eventRuleBO.getGeneralRule())) {
            //当前没有针对统一规则的验证逻辑，统一规则用QL判断，后续做业务上线验证，目前默认成功
            return true;
        }
        if (CollectionUtils.isNotEmpty(eventRuleBO.getFieldRuleList())) {
            for (DecisionEventRuleBO.FieldRule fieldRule : eventRuleBO.getFieldRuleList()) {
                //如果是in or notin 的情况，需要判断 value是否为数组类型
                if (fieldRule.getOperateEnum() == SelectOperateEnum.IN || fieldRule.getOperateEnum() == SelectOperateEnum.NOT_IN) {
                    if (!JSONUtil.isTypeJSONArray(fieldRule.getOperateValue())) {
                        log.error("EventDecisionService#propertyRuleCheck 基础决策规则验证失败 inList和notinList操作符只支持数组类型 eventRuleBO：【{}】", JSONObject.toJSONString(eventRuleBO));
                        throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "inList和notinList操作符只支持数组类型");
                    }
                }
                if (fieldRule.getTypeEnum().equals(EventFieldTypeEnum.STRING)) {
                    if (fieldRule.getOperateEnum() == SelectOperateEnum.GT || fieldRule.getOperateEnum() == SelectOperateEnum.LT
                            || fieldRule.getOperateEnum() == SelectOperateEnum.GTOE || fieldRule.getOperateEnum() == SelectOperateEnum.LTOE) {
                        log.error("EventDecisionService#propertyRuleCheck 基础决策规则验证失败 字符串类型不支持大于、小于、大于等于、小于等于操作符 eventRuleBO：【{}】", JSONObject.toJSONString(eventRuleBO));
                        throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "字符串类型不支持大于、小于、大于等于、小于等于操作符");
                    }
                }
                //其他还可以做更详细的，例如 score > 50 && score < 40 这种错误情况
                try {
                    fieldTypeCheck(fieldRule.getTypeEnum(), fieldRule.getOperateValue());
                } catch (Exception e) {
                    log.error("EventDecisionService#propertyRuleCheck 基础决策规则验证失败 字段类型转换异常 eventRuleBO：【{}】", JSONObject.toJSONString(eventRuleBO), e);
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 字段校验  - 主要判断是否能够转换为对应类型
     *
     * @param fieldType
     * @param operateValue
     * @throws Exception
     */
    private void fieldTypeCheck(EventFieldTypeEnum fieldType, String operateValue) {
        switch (fieldType) {
            case LONG:
                Long.parseLong(operateValue);
                return;
            case DOUBLE:
                Double.parseDouble(operateValue);
                return;
            case ARRAY:
                JSONObject.parseArray(operateValue);
                return;
            case BOOLEAN:
                Boolean.parseBoolean(operateValue);
                return;
            case DATE:
                DateUtils.dateTime(operateValue, DateUtils.YYYY_MM_DD_HH_MM_SS);
                return;
            case DECIMAL:
                new BigDecimal(operateValue);
                return;
            case OBJECT:
                JSONObject.parseObject(operateValue);
        }
    }
}
